package dxbridge.model.impl;

import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;


import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import dxbridge.model.IDataSet;
import dxbridge.model.ISchema;
import dxbridge.model.ITable;

public class Schema implements ISchema {

    public static String XPATH_SELECT_ALL_TABLE_NAME = "//xsd:element[@prodata:tableName]";

    public static String XPATH_SELECT_TABLE_FIELDS_PRE = "//xsd:element[@prodata:tableName and @name=\"";
    public static String XPATH_SELECT_TABLE_FIELDS_SUR = "\"]/xsd:complexType/xsd:sequence/xsd:element[not(@prodata:tableName)]";

    public static String XPATH_SELECT_TABLE_PUKEY_PRE = "//xsd:unique[@prodata:primaryIndex=\"true\" and @name=\"";
    public static String XPATH_SELECT_TABLE_PUKEY_SUR = "\"]/xsd:field";

    public static String XPATH_SELECT_TABLE_KEYREF = "//xsd:keyref";

    @Override
    public IDataSet getDataSet() {
        return this.dataset;
    }

    /**
     * @param schemaFile
     * @return
     * @throws DocumentException
     */
    @SuppressWarnings("unchecked")
    public static ISchema SchemaFactory(File schemaFile)
            throws DocumentException {

        Schema schema = new Schema();
        IDataSet dataset = new DataSet();

        /* read schema file */
        Document schemaDoc = new SAXReader().read(schemaFile);
        /* table names list */

        dataset = new DataSet();

        /* select tables */
        List<Node> tableNodes = schemaDoc
                .selectNodes(XPATH_SELECT_ALL_TABLE_NAME);
        for (Node nTable : tableNodes) {
            /* for each table , create Table instance */
            ITable t = new Table(nTable.valueOf("@name"));
            /* set table depth for one table */
            int depth = -5;
            Node temp = nTable;
            while (temp != null) {
                temp = temp.getParent();
                depth++;
            }
            t.setDepth(depth);

            /* select fields for one table */
            List<Node> fieldNodes = schemaDoc
                    .selectNodes(XPATH_SELECT_TABLE_FIELDS_PRE
                            + t.getTableName() + XPATH_SELECT_TABLE_FIELDS_SUR);

            List<String> fields = new LinkedList<String>();
            for (Node nField : fieldNodes) {
                fields.add(nField.valueOf("@name"));
            }

            t.setFieldNames(fields.toArray(new String[0]));

            /* select key fields for one table */
            List<Node> pukeyNodes = schemaDoc
                    .selectNodes(XPATH_SELECT_TABLE_PUKEY_PRE
                            + t.getTableName() + XPATH_SELECT_TABLE_PUKEY_SUR);
            List<String> pukey = new LinkedList<String>();
            for (Node nField : pukeyNodes) {
                pukey.add(nField.valueOf("@xpath"));
            }

            int[] keyIndex = new int[pukey.size()];
            int count = 0;
            for (String str : pukey) {
                keyIndex[count++] = fields.indexOf(str);
            }

            t.setKeyIndex(keyIndex);

            dataset.addTable(t);
        }

        /* deal with foreign relationship */
        {
            List<Node> refNodes = schemaDoc
                    .selectNodes(XPATH_SELECT_TABLE_KEYREF);
            for (Node refNode : refNodes) {
                ITable table = dataset.getTable(refNode.selectSingleNode(
                        "./xsd:selector").valueOf("@xpath").replace(".//", ""));
                Map<String, String> map = new HashMap<String, String>();
                ITable foreignTable = dataset.getTable(refNode
                        .valueOf("@refer"));
                List<Node> refFields = refNode.selectNodes("./xsd:field");
                int count = 0;
                for (Node nField : refFields) {
                    String field = nField.valueOf("@xpath");
                    map.put(table.getTableName() + "." + field, foreignTable
                            .getTableName()
                            + "."
                            + foreignTable.getFieldName(foreignTable
                                    .getKeyIndex()[count++]));
                }
                table.setForeignKeyMap(map);
            }
        }

        schema.setDataset(dataset);

        return schema;
    }

    public void setDataset(IDataSet ds) {
        this.dataset = ds;
    }

    public static void main(String args[]) throws DocumentException {
        ISchema sch = Schema.SchemaFactory(new File("browse.xsd"));
        sch.getDataSet().printTest();

    }

    private IDataSet dataset;
}
